package net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.prime;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import java.util.LinkedHashMap;
import java.util.Map;

import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.LCONST_0;

/**
 * @author daimao  Date: 16/4/21 Time: 下午11:22
 * @version $Id$
 */
public class PrimesAppliers {

    static final Map<Class<?>, BoxType> primTypeBoxInfoMap = new LinkedHashMap<>();

    static {
        primTypeBoxInfoMap.put(byte.class, new BoxType(Byte.class, byte.class, ICONST_0));
        primTypeBoxInfoMap.put(short.class, new BoxType(Short.class, short.class, ICONST_0));
        primTypeBoxInfoMap.put(int.class, new BoxType(Integer.class, int.class, ICONST_0));
        primTypeBoxInfoMap.put(long.class, new BoxType(Long.class, long.class, LCONST_0));
        primTypeBoxInfoMap.put(float.class, new BoxType(Float.class, float.class, FCONST_0));
        primTypeBoxInfoMap.put(double.class, new BoxType(Double.class, double.class, DCONST_0));
    }

    public static boolean isPrimeType(Class<?> type) {
        return primTypeBoxInfoMap.containsKey(type);
    }

    public static BoxType findBoxType(Class<?> type) {
        return primTypeBoxInfoMap.get(type);
    }

    public static void applyCode(Class<?> type, MethodVisitor mv) throws Exception {
        primTypeBoxInfoMap.get(type).applyCode(mv);
    }

    public static void applyDefaultRetCodeOntoStack(Class<?> type, MethodVisitor mv) {
        if (isPrimeType(type)) {
            BoxType boxType = primTypeBoxInfoMap.get(type);
            mv.visitInsn(boxType.defaultTypeLoadOpcode);
        } else {
            mv.visitInsn(ACONST_NULL);
        }
    }

    public static class BoxType {
        private final String boxTypeInternalName;
        private final String boxTypeDesc;
        private final String primTypeDesc;
        private final int defaultTypeLoadOpcode;
        private static final String valueOf = "valueOf";

        public BoxType(Class<?> boxType, Class<?> primType, int defaultTypeLoadOpcode) {
            boxTypeInternalName = Type.getInternalName(boxType);
            boxTypeDesc = Type.getDescriptor(boxType);
            primTypeDesc = Type.getDescriptor(primType);
            this.defaultTypeLoadOpcode = defaultTypeLoadOpcode;
        }

        void applyCode(MethodVisitor mv) {
            mv.visitMethodInsn(INVOKESTATIC, boxTypeInternalName, valueOf, "(" + primTypeDesc + ")" + boxTypeDesc, false);
        }

        public String getBoxTypeInternalName() {
            return boxTypeInternalName;
        }

        public String getBoxTypeDesc() {
            return boxTypeDesc;
        }

        public String getPrimTypeDesc() {
            return primTypeDesc;
        }

        public int getDefaultTypeLoadOpcode() {
            return defaultTypeLoadOpcode;
        }
    }
}